import dayjs from 'dayjs'

/**
 * 指定字符集生成随机字符串
 * @param length 生成的长度
 * @param chars 指定字符集
 * @returns
 */
export function randomString(length, chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') {
	let result = ''
	for (let i = length; i > 0; --i) result += chars[Math.floor(Math.random() * chars.length)]
	return result
}

/**
 * 深拷贝
 * @param {Object} target
 */
export function deepClone(target) {
	// WeakMap作为记录对象Hash表（用于防止循环引用）
	const map = new WeakMap()

	// 判断是否为object类型的辅助函数，减少重复代码
	function isObject(target) {
		return (typeof target === 'object' && target) || typeof target === 'function'
	}

	function clone(data) {
		// 基础类型直接返回值
		if (!isObject(data)) {
			return data
		}

		// 日期或者正则对象则直接构造一个新的对象返回
		if ([Date, RegExp].includes(data.constructor)) {
			return new data.constructor(data)
		}

		// 处理函数对象
		if (typeof data === 'function') {
			return new Function('return ' + data.toString())()
		}

		// 如果该对象已存在，则直接返回该对象
		const exist = map.get(data)
		if (exist) {
			return exist
		}

		// 处理Map对象
		if (data instanceof Map) {
			const result = new Map()
			map.set(data, result)
			data.forEach((val, key) => {
				// 注意：map中的值为object的话也得深拷贝
				if (isObject(val)) {
					result.set(key, clone(val))
				} else {
					result.set(key, val)
				}
			})
			return result
		}

		// 处理Set对象
		if (data instanceof Set) {
			const result = new Set()
			map.set(data, result)
			data.forEach((val) => {
				// 注意：set中的值为object的话也得深拷贝
				if (isObject(val)) {
					result.add(clone(val))
				} else {
					result.add(val)
				}
			})
			return result
		}

		// 收集键名（考虑了以Symbol作为key以及不可枚举的属性）
		const keys = Reflect.ownKeys(data)
		// 利用 Object 的 getOwnPropertyDescriptors 方法可以获得对象的所有属性以及对应的属性描述
		const allDesc = Object.getOwnPropertyDescriptors(data)
		// 结合 Object 的 create 方法创建一个新对象，并继承传入原对象的原型链， 这里得到的result是对data的浅拷贝
		const result = Object.create(Object.getPrototypeOf(data), allDesc)

		// 新对象加入到map中，进行记录
		map.set(data, result)

		// Object.create()是浅拷贝，所以要判断并递归执行深拷贝
		keys.forEach((key) => {
			const val = data[key]
			if (isObject(val)) {
				// 属性值为 对象类型 或 函数对象 的话也需要进行深拷贝
				result[key] = clone(val)
			} else {
				result[key] = val
			}
		})
		return result
	}

	return clone(target)
}

const ensureDecodeURIComponent = (url) => {
	if (url.startsWith('%')) {
		return ensureDecodeURIComponent(decodeURIComponent(url))
	}
	return url
}

/**
 * 扁平化树形结构数据
 * @param {Array} arrs 树形数据
 * @param {string} childs 树形数据子数据的属性名,常用'children'
 * @param {Array} attrArr 需要提取的公共属性数组(默认是除了childs的全部属性)
 * @returns
 */
export function extractTree(arrs, childs, attrArr) {
	let attrList = []
	if (arrs.length === 0 || !Array.isArray(arrs)) return []
	if (typeof childs !== 'string') return []
	if (!Array.isArray(attrArr) || (Array.isArray(attrArr) && !attrArr.length)) {
		attrList = Reflect.ownKeys(arrs[0])
		attrList.splice(attrList.indexOf(childs), 1)
	} else {
		attrList = attrArr
	}
	const list = []
	const getObj = (arr) => {
		arr.forEach(function(row) {
			const obj = {}
			attrList.forEach((item) => {
				obj[item] = row[item]
			})
			list.push(obj)
			if (row[childs]) {
				getObj(row[childs])
			}
		})
		return list
	}
	return getObj(arrs)
}

export function valueToLabel(value, config) {

	if (value === null || value === undefined || value === '' || !config) return value

	if (Array.isArray(value)) {
		let resArr = []
		value.forEach((valueItem) => {
			config.forEach((configItem) => {
				if (configItem.value === valueItem) resArr.push(configItem.label)
			})
		})
		return resArr
	} else {
		const findRes = config.find((item) => {
			return item.value === value
		})
		if (findRes) return findRes.label
		return value
	}
}

/**
 * 名字脱敏
 * @param name
 */
export function nameConvert(name) {
	let userName = ''
	if (name.length == 2) {
		userName = name.substring(0, 1) + '*' //截取name 字符串截取第一个字符，
	} else if (name.length == 3) {
		userName = name.substring(0, 1) + '*' + name.substring(2, 3) //截取第一个和第三个字符
	} else if (name.length > 3) {
		userName = name.substring(0, 1) + '*' + '*' + name.substring(3, name.length) //截取第一个和大于第4个字符
	}
	return userName
}

/**
 * 防抖
 * @param fn 需要执行的函数
 * @param delay 延时（ms）
 * @param immediate 是否立即执行
 */
export function debounce(fn, delay, immediate = false) {
	let timer = null

	let isInvoke = false

	const _debounce = function(...args) {
		if (timer) clearTimeout(timer)

		if (!isInvoke && immediate) {
			fn.apply(this, args)
			isInvoke = true
			return
		}

		timer = setTimeout(() => {
			fn.apply(this, args)
			timer = null
			isInvoke = false
		}, delay)
	}

	_debounce.cancel = function() {
		if (timer) clearTimeout(timer)
		timer = null
		isInvoke = false
	}

	return _debounce
}

export function getUrlValue(value) {
	// 从第一个?开始，且不包括#之后，并截取掉?的部分
	const query = location.search.substring(1);
	// 从#开始的部分，并转换成数组
	const hash = location.hash.split("?");
	// query和hash均没有参数
	if (!query && hash.length < 2) {
		return ''
	}
	// 先取query部分的参数进行匹配
	let vars = query.split("&")
	for (let i = 0; i < vars.length; i++) {
		const pair = vars[i].split("=")
		if (pair[0] == value) {
			return pair[1]
		}
	}
	// query没有参数，或者有参数但没找到，则取hash部分的参数
	if (!hash[1]) {
		return ''
	}
	vars = hash[1].split("&")
	for (let i = 0; i < vars.length; i++) {
		const pair = vars[i].split("=")
		if (pair[0] == value) {
			return pair[1]
		}
	}
	return ''
}

/**
 * @deprecated 接收一个表示分钟数的参数，然后返回当前时间加上传入分钟数之后对应的年、月、日、时、分、秒
 * @param {number} minutes 分钟
 */
export function getTimeAfterMinutes(minutes) {
	// 获取当前时间的Date对象
	let currentDate = new Date();
	// 在当前时间基础上增加指定的分钟数（将分钟数转换为毫秒数后增加）
	currentDate.setTime(currentDate.getTime() + minutes * 60 * 1000);
	// 获取增加分钟数后的年份
	let year = currentDate.getFullYear();
	// 获取增加分钟数后的月份（注意：返回值是0-11，0表示1月，11表示12月，实际使用时通常要加1）
	let month = currentDate.getMonth() + 1;
	// 获取增加分钟数后的日期
	let day = currentDate.getDate();
	// 获取增加分钟数后的小时
	let hour = currentDate.getHours();
	// 获取增加分钟数后的分钟
	let minute = currentDate.getMinutes();
	// 获取增加分钟数后的秒数
	let second = currentDate.getSeconds();
	return {
		year,
		month,
		day,
		hour,
		minute,
		second
	}
}

export function formatData(data, formatter = 'YYYY-MM-DD HH:mm:ss') {
	return dayjs(data).format(formatter)
}